// Dr. Reese's IDT Stuff

/* ============================================================ */
/* File: INIT-IDT.C */
/* */
/* ============================================================ */
#include <lib.h>
#include <system.h>
#define _HW_INT_BASE (256-16) /* Use last 16 vectors */
/* -------------------------------------------------------------- */
/* Format of an entry in the Interrupt Descriptor Table (IDT). */
/* Note: Not needed if you use SetISR (see below). */
/* -------------------------------------------------------------- */
typedef struct IDT_DESCRIPTOR
{
	WORD16 offset_0 ; /* Offset, bits 15-0 */
	WORD16 selector ; /* Selector value */
	BYTE8 not_used ; /* Must be set to 0's */
	BYTE8 flags; /* Access rights, etc. */
	WORD16 offset_16; /* Offset, bits 31-16 */
} __attribute__((packed)) IDT_DESCRIPTOR ;
/* -------------------------------------------------------------- */
/* Operand used by the LIDT instruction. */
/* Note: Not needed if you use SetISR (see below). */
/* -------------------------------------------------------------- */
typedef struct IDT_INFO
{
	WORD16 limit ;
	DWORD32 addrs ;
} __attribute__((packed)) IDT_INFO ;

extern WORD16 code_selector __asm__("code_selector") ;
#define INTEL_INT(n) (0 <= n && n < 32)
#define PIC1_INT(n) (IRQ2INT(0) <= n && n <= IRQ2INT(7))
#define PIC2_INT(n) (IRQ2INT(8) <= n && n <= IRQ2INT(15))
#define PRIVATE static

PRIVATE void Abort(char *msg)
{
	CLI ;
	puts(msg) ;
	puts("; system halted.") ;
	for (;;) ;
}

PRIVATE void ISR00(void) { Abort("Divide error"); }
PRIVATE void ISR01(void) { Abort("Debug Interrupt"); }
PRIVATE void ISR02(void) { Abort("Non-Maskable Interrupt"); }
PRIVATE void ISR03(void) { Abort("Breakpoint Interrupt"); }
PRIVATE void ISR04(void) { Abort("Overflow"); }
PRIVATE void ISR05(void) { Abort("BOUND Range Exceeded"); }
PRIVATE void ISR06(void) { Abort("Invalid Opcode"); }
PRIVATE void ISR07(void) { Abort("No Math CoProcessor"); }
PRIVATE void ISR08(void) { Abort("Double Fault"); }
PRIVATE void ISR09(void) { Abort("Math CoProcessor Overrun"); }
PRIVATE void ISR10(void) { Abort("Invalid TSS"); }
PRIVATE void ISR11(void) { Abort("Segment Not Present"); }
PRIVATE void ISR12(void) { Abort("Stack Segment Fault"); }
PRIVATE void ISR13(void) { Abort("General Protection"); }
PRIVATE void ISR14(void) { Abort("Page Fault"); }
PRIVATE void ISR16(void) { Abort("Math CoProcessor Error"); }
PRIVATE void ISR17(void) { Abort("Alignment Check"); }
PRIVATE void ISR18(void) { Abort("Machine Check"); }
PRIVATE void ISR19(void) { Abort("SIMD floating-point exception"); }
PRIVATE void ISR_Rsvd(void) { Abort("Intel Reserved Interrupt"); }
PRIVATE void ISR_User(void) { Abort("Unassigned Interrupt"); }

PRIVATE ISR _intel_isr[] =
{
	ISR00, /* Divide by zero */
	ISR01, /* Debug exception */
	ISR02, /* NMI */
	ISR03, /* One byte interrupt */
	ISR04, /* Interrupt on overflow */
	ISR05, /* Array bounds error */
	ISR06, /* Invalid opcode */
	ISR07, /* Math not available */
	ISR08, /* Double fault */
	ISR09, /* Math segment overflow */
	ISR10, /* Invalid TSS */
	ISR11, /* Segment not present */
	ISR12, /* Stack fault */
	ISR13, /* General protection */
	ISR14, /* Page fault */
	ISR_Rsvd, /* Reserved */
	ISR16, /* Math error */
	ISR17, /* Alignment Check */
	ISR18, /* Machine Check */
	ISR19, /* SIMD floating-point except.*/
	ISR_Rsvd, /* Reserved */
	ISR_Rsvd, /* Reserved */
	ISR_Rsvd, /* Reserved */
	ISR_Rsvd, /* Reserved */
	ISR_Rsvd, /* Reserved */
	ISR_Rsvd, /* Reserved */
	ISR_Rsvd, /* Reserved */
	ISR_Rsvd, /* Reserved */
	ISR_Rsvd, /* Reserved */
	ISR_Rsvd, /* Reserved */
	ISR_Rsvd, /* Reserved */
	ISR_Rsvd /* Reserved */
} ;

PRIVATE IDT_DESCRIPTOR idt[256] ;
PRIVATE IDT_INFO idt_info = { sizeof(idt) - 1, (DWORD32) idt } ;

int IRQ2INT(int irq)
{
	return _HW_INT_BASE + irq ;
}

ISR GetISR(int int_numb)
{
	IDT_DESCRIPTOR *p = &idt[int_numb] ;
	DWORD32 offset ;
	offset = (((DWORD32) p->offset_16) << 16) + p->offset_0 ;
	return (ISR) offset ;
}

void SetISR(int int_numb, ISR isr)
{
	IDT_DESCRIPTOR *p = &idt[int_numb] ;
	p->offset_0 = ((WORD16 *) &isr)[0] ;
	p->offset_16 = ((WORD16 *) &isr)[1] ;
}

/* Hide this function's name from C programs! */
//void Init_IDT(void) __asm__ ("Init_IDT") ;
void Init_IDT(void)
{
	extern void ISR_PIC1(void) __asm("ISR_PIC1") ;
	extern void ISR_PIC2(void) __asm("ISR_PIC2") ;
	IDT_DESCRIPTOR *p ;
	int i ;
	p = idt ;
	for (i = 0; i < ENTRIES(idt); i++, p++)
	{
		ISR isr ;
		p->selector = code_selector ;
		p->not_used = 0x00 ;
		p->flags = 0x8E ;
		if (INTEL_INT(i)) isr = _intel_isr[i] ;
		else if (PIC1_INT(i)) isr = ISR_PIC1 ;
		else if (PIC2_INT(i)) isr = ISR_PIC2 ;
		else isr = ISR_User ;
		p->offset_0 = ((WORD16 *) &isr)[0] ;
		p->offset_16 = ((WORD16 *) &isr)[1] ;
	}
	__asm__ __volatile__ ("lidt %0" : : "m" (idt_info)) ;
}

